/**
 * Copyright 2020 Huawei Technologies Co., Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef DAVINCI_AICPU_OPTIMIZER_H_
#define DAVINCI_AICPU_OPTIMIZER_H_

#include <unordered_map>
#include <vector>
#include "common/aicpu_graph_optimizer/optimizer.h"
#include "graph/compute_graph.h"
#include "graph/detail/attributes_holder.h"
#include "graph/op_desc.h"
#include "proto/cpu_attr.pb.h"
#include "proto/cpu_node_def.pb.h"
#include "proto/cpu_tensor.pb.h"
#include "proto/cpu_tensor_shape.pb.h"
#include "util/constant.h"

namespace aicpu {
using OptimizerPtr = std::shared_ptr<Optimizer>;

class AicpuOptimizer : public Optimizer {
 public:
  /**
   * get optimizer instance
   * @return ptr stored aicpu optimizer
   */
  static OptimizerPtr Instance();

  /**
   * Destructor
   */
  virtual ~AicpuOptimizer() = default;

  /**
   * optimizer fused Graph, find aicpu ops which can be fused and fuse them
   * @param graph, Compute graph
   * @param all_op_info, map stored all aicpu ops information
   * @return result is success or not
   */
  ge::Status OptimizeFusedGraph(
      ge::ComputeGraph &graph,
      const std::map<std::string, OpFullInfo> &all_op_info) const override;

  /**
   * init optimizer
   * @return status whether this operation success
   */
  ge::Status Initialize() override;

 private:
  /**
   * get aicpu ops' defination
   * @param op_desc_ptr, ptr stored op's description
   * @param node_def, stored node
   * @return result is success or not
   */
  ge::Status BuildAicpuNodeDef(const ge::OpDescPtr &op_desc_ptr,
                               aicpuops::NodeDef &node_def) const;

  /**
   * Get AttrValue from ge value type
   * @param op_desc_ptr Ge op desc
   * @param attr_name  Set attr_value for this attrname
   * @param ge_value_type Value type from ge
   * @param attr_value This func going to fill this param
   * @return result is success or not
   */
  ge::Status GetAttrValueFromGe(const ge::OpDescPtr &op_desc_ptr,
                                const string &attr_name,
                                const ge::GeAttrValue::ValueType ge_value_type,
                                aicpuops::AttrValue &attr_value) const;

  /**
   * set type of value with string
   * @param op_desc_ptr Ge op desc
   * @param attr_name  Set attr_value for this attrname
   * @param attr_value This func going to fill this param
   * @return result is success or not
   */
  ge::Status SetValueTypeString(const ge::OpDescPtr &op_desc_ptr,
                                const std::string &attr_name,
                                aicpuops::AttrValue &attr_value) const;

  /**
   * set type of value with float
   * @param op_desc_ptr Ge op desc
   * @param attr_name  Set attr_value for this attrname
   * @param attr_value This func going to fill this param
   * @return result is success or not
   */
  ge::Status SetValueTypeFloat(const ge::OpDescPtr &op_desc_ptr,
                               const std::string &attr_name,
                               aicpuops::AttrValue &attr_value) const;
  /**
   * set type of value with bool
   * @param op_desc_ptr Ge op desc
   * @param attr_name  Set attr_value for this attrname
   * @param attr_value This func going to fill this param
   * @return result is success or not
   */
  ge::Status SetValueTypeBool(const ge::OpDescPtr &op_desc_ptr,
                              const std::string &attr_name,
                              aicpuops::AttrValue &attr_value) const;
  /**
   * set type of value with int
   * @param op_desc_ptr Ge op desc
   * @param attr_name  Set attr_value for this attrname
   * @param attr_value This func going to fill this param
   * @return result is success or not
   */
  ge::Status SetValueTypeInt(const ge::OpDescPtr &op_desc_ptr,
                             const std::string &attr_name,
                             aicpuops::AttrValue &attr_value) const;

  /**
   * Add Aicpu tensor value and set in.
   * @param op_desc_ptr Ge op desc
   * @param attr_name Aicpu attr name
   * @param attr_value Aicpu tesor value
   * @param result is success or failed.
   */
  ge::Status SetValueTypeAicpuTensor(const ge::OpDescPtr &op_desc_ptr,
                                     const std::string &attr_name,
                                     aicpuops::AttrValue &attr_value) const;

  /**
   * Add Aicpu type value and set in.
   * @param op_desc_ptr Ge op desc
   * @param attr_name Aicpu attr name
   * @param attr_value Aicpu type value
   * @param result is success or failed.
   */
  ge::Status SetValueTypeAicpuDataType(const ge::OpDescPtr &op_desc_ptr,
                                       const string &attr_name,
                                       aicpuops::AttrValue &attr_value) const;

  /**
   * Add Aicpu list(shape) value and set in.
   * @param op_desc_ptr Ge op desc
   * @param attr_name Aicpu attr name
   * @param attr_value Aicpu list(shape) value
   * @param result is success or failed.
   */
  ge::Status SetValueTypeListAicpuShape(const ge::OpDescPtr &op_desc_ptr,
                                        const std::string &attr_name,
                                        aicpuops::AttrValue &attr_value) const;

  /**
   * Add Aicpu list(type) value and set in.
   * @param op_desc_ptr Ge op desc
   * @param attr_name Aicpu attr name
   * @param attr_value Aicpu list(type) value
   * @param result is success or failed.
   */
  ge::Status SetValueTypeListAicpuDataType(
      const ge::OpDescPtr &op_desc_ptr, const std::string &attr_name,
      aicpuops::AttrValue &attr_value) const;

  /**
   * Add Aicpu list(int) value and set in.
   * @param node Ge node
   * @param attr_name Aicpu attr name
   * @param attr_value Aicpu list(int) value
   * @param result is success or failed.
   */
  ge::Status SetValueTypeListInt(const ge::OpDescPtr &op_desc_ptr,
                                 const string &attr_name,
                                 aicpuops::AttrValue &attr_value) const;

  /**
   * Add Aicpu list(string) value and set in.
   * @param op_desc_ptr Op desc pointer
   * @param attr_name Aicpu Attr name
   * @param attr_value Aicpu Attr value
   * @return result is success or not
   */
  ge::Status SetValueTypeListString(const ge::OpDescPtr &op_desc_ptr,
                                    const std::string &attr_name,
                                    aicpuops::AttrValue &attr_value) const;

  /**
   * Add Aicpu list(float) value and set in.
   * @param op_desc_ptr Op desc pointer
   * @param attr_name Aicpu Attr name
   * @param attr_value Aicpu Attr value
   * @return result is success or not
   */
  ge::Status SetValueTypeListFloat(const ge::OpDescPtr &op_desc_ptr,
                                   const std::string &attr_name,
                                   aicpuops::AttrValue &attr_value) const;

  /**
   * Read bin file
   * @param op_desc_ptr Op desc pointer
   * @param bin_folder_path bin file path
   * @param op_full_info op full info
   * @param graph_id graph id
   * @param exist_graph_id exist graph id
   * @return bool flag
   */
  bool PackageBinFile(ge::OpDescPtr op_desc_ptr,
                      const std::string &bin_folder_path,
                      const OpFullInfo &op_full_info, uint32_t graph_id,
                      bool exist_graph_id) const;

  /**
   * Get custom aicpu kernel so path
   * @param null
   * @return string path
   */
  const std::string GetCustKernelSoPath() const;

  /**
   * Get cpu kernel so path (libcpu_kernels.so)
   * @param null
   * @return string path
   */
  const std::string GetCpuKernelSoPath() const;

  /**
   * Init load cpu kernels type
   */
  void InitLoadCpuKernelsType();

  /**
   * Check so whether need load in model
   * @param op_full_info op full info
   * @param file_path file path
   * @return true or false
   */
  bool CheckSoNeedLoadInModel(OpFullInfo &op_full_info,
                              std::string &file_path) const;

  /**
   * Get bin file name
   * @param op_full_info op full info
   * @param bin_folder_path bin folder path
   * @param bin_file_name bin file name
   * @param graph_id graph id
   * @return result is success or not
   */
  ge::Status GetBinFileName(const OpFullInfo &op_full_info,
                            const std::string &bin_folder_path,
                            std::string &bin_file_name) const;

  ge::Status SetCustKernelBinFile(
      ge::OpDescPtr op_desc_ptr,
      const std::map<std::string, OpFullInfo> &all_op_info, uint32_t graph_id,
      bool exist_graph_id) const;

  void CheckAndSetSocVersion(const std::string &soc_version_from_ge);

 private:
  // singleton instance
  static OptimizerPtr instance_;

  // load type for libcpu_kernels.so
  uint64_t load_type_for_cpu_kernels_ = kDefaultLoadTypeForCpuKernels;
};
}  // namespace aicpu

#endif  // DAVINCI_AICPU_OPTIMIZER_H
